#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "../cominc/cli.h"
#include "../cominc/ctask.h"

/* 外部导入方法 */
void CliPutChar(char ch);

/* 外部导入的<命令-回调>键值对列表 */
static CmdPair* BSP_CMD_LIST = NULL;
static int BSP_CMD_LIST_LEN = 0;

/* 内部实现方法 */
void CliPutStr(const char *str, ...);
static int ExeCliCmd(const char* rxBuffer, size_t rxLen); // 处理输入缓冲区字符
static int CliBackSpace(const char * str, size_t len); // 命令行退格
static void UpdateHistoryCmd(void); // 更新历史命令
static int CliUp(const char * str, size_t len); // 上翻历史命令
static int CliDown(const char * str, size_t len); // 下翻历史命令
static int CliReturn(const char * str, size_t len); // 命令行换行
static int DefaultFlushTask(const char *str, size_t len); // 注册在用户命令之后的清Rx任务
void CliClrLine(void); // 命令行清空当前行
void CliCursorTo(size_t y, size_t x); // 命令行清空当前行
void CliSaveCursorPos(void);
void CliRestoreCursorPos(void);
void ClrVtScreen(void); // 命令行清屏

#define CLI_KEY_LIST_LEN 16
static CmdPair BSP_KEY_LIST[CLI_KEY_LIST_LEN] = {
    {CMD_KEY_UP,   CliUp},
    {CMD_KEY_DOWN, CliDown},
    {CMD_KEY_BK,   CliBackSpace},
    {CMD_KEY_RTN,  CliReturn},
};

typedef enum {
  LOG_NOINPUT,
  LOG_INFO,
  LOG_USR_TYPING,
  LOG_EVENT,
  LOG_BAR,
  LOG_CRITICAL,
} LOG_TYPE;
static LOG_TYPE lastLogType = LOG_NOINPUT;

/* Dump命令默认弱实现 */
int __attribute__((weak)) Dump(const char *str, size_t len)
{
    LOG("\t\tDump\r\nVer <%s>\r\n", TIMESTAMPER_VER);
    return 0;
}

/* 时戳默认弱实现 */
const char* __attribute__((weak)) CliTimeStamp() { return "00:00:00.000"; }

/* 外部方法接口 */
void CliInit(CmdPair* cmdList, int cmdListLen)
{
    BSP_CMD_LIST = cmdList;
    BSP_CMD_LIST_LEN = cmdListLen;

    ClrVtScreen();
	  CliPutStr("\033[33m"); // 设置为黄色
	  CliPutStr("\r\nBSP %s\r\n", TIMESTAMPER_VER);
    CliPutStr("\033[0m"); // 取消格式
    FlushRx();
}

void CliRegKeyAction(CmdPair cmdList)
{
    CmdPair* pCmdSlot = BSP_KEY_LIST;
    for (; pCmdSlot != BSP_KEY_LIST + CLI_KEY_LIST_LEN; pCmdSlot++) {
        if (strlen(pCmdSlot->cmd) == 0) { // 寻找空闲命令槽位
            break;
        }
    }
    memcpy(pCmdSlot, &cmdList, sizeof(CmdPair));
}

static char CLI_RX_BUFFER[CMD_LEN_MAX];
static size_t RX_BUFF_IDX;
void CliProcByte(char ch)
{
    lastLogType = LOG_USR_TYPING;
    // 避免缓冲区越界，并更新缓存区
    if (RX_BUFF_IDX >= CMD_LEN_MAX) {
        CLI_RX_BUFFER[CMD_LEN_MAX - 1] = 0;
        CliClrLine();
        FlushRx();
    }
    CLI_RX_BUFFER[RX_BUFF_IDX++] = ch;
    // 判断是否控制字符，若是则执行
    uint32_t isCtrl = 0;
    for (size_t idx = 0; idx < CLI_KEY_LIST_LEN; idx++) {
        const CmdPair* pCmd = BSP_KEY_LIST + idx;
        size_t keyLen = strlen(pCmd->cmd);
        size_t rxLen = strlen(CLI_RX_BUFFER);
        if (keyLen == 0 || keyLen > rxLen) {
            continue;
        }
        size_t inputStrLen = rxLen - keyLen;
        const char* keyInput = CLI_RX_BUFFER + inputStrLen;
        // 对比最后组合键长度个字符，判断是否组合键
        if (memcmp(pCmd->cmd, keyInput, keyLen) == 0) {
            RegTask(pCmd->exe, CLI_RX_BUFFER, inputStrLen);
            isCtrl = 1;
            break;
        }
    }
    // 控制字符不需要显示
    if (!isCtrl) {
        // 保存字符并反显
        CliPutChar(ch);
    }
}

void CliPutStr(const char *str, ...)
{
    char CLI_TX_BUFFER[CMD_LEN_MAX] = {0};
    va_list args;
    va_start(args, str);
    vsprintf(CLI_TX_BUFFER, str, args);
    va_end(args);
    char* pChar = CLI_TX_BUFFER;
    while ((*pChar != 0) && (pChar - CLI_TX_BUFFER < CMD_LEN_MAX)) {
        CliPutChar(*pChar++);
    }
}

int LOG(const char *str, ...)
{
    char CLI_TX_BUFFER[CMD_LEN_MAX];
    va_list args;
    va_start(args, str);
    vsprintf(CLI_TX_BUFFER, str, args);
    va_end(args);
    if (lastLogType != LOG_INFO) {
        CliPutStr("\r\n");
    }
    CliPutStr("\033[32m%s\033[0m\r\n", CLI_TX_BUFFER); // 设置为绿色
    lastLogType = LOG_INFO;
    return 0;
}

int LOGStr(const char *str, ...)
{
    char CLI_TX_BUFFER[CMD_LEN_MAX];
    va_list args;
    va_start(args, str);
    vsprintf(CLI_TX_BUFFER, str, args);
    va_end(args);
    if (lastLogType != LOG_INFO) {
        CliPutStr("\r\n");
    }
    CliPutStr("\033[32m%s\033[0m", CLI_TX_BUFFER); // 设置为绿色
    lastLogType = LOG_INFO;
    return 0;
}

int LOGCritcal(const char *str, ...)
{
    char CLI_TX_BUFFER[CMD_LEN_MAX];
    va_list args;
    va_start(args, str);
    vsprintf(CLI_TX_BUFFER, str, args);
    va_end(args);
    if (lastLogType != LOG_CRITICAL) {
        CliPutStr("\r\n");
    }
    CliPutStr("\033[41m%s\033[0m\r\n", CLI_TX_BUFFER); // 设置为红色
    lastLogType = LOG_CRITICAL;
    return 0;
}

int LogEvent(const char *str, ...)
{
    char CLI_TX_BUFFER[CMD_LEN_MAX];
    va_list args;
    va_start(args, str);
    vsprintf(CLI_TX_BUFFER, str, args);
    va_end(args);
    if (lastLogType != LOG_NOINPUT && lastLogType != LOG_EVENT) {
        CliPutStr("\r\n");
    }
    CliClrLine();
    CliPutStr("\033[33m"); // 设置为黄色
    CliPutStr("%s %s", CliTimeStamp(), CLI_TX_BUFFER);
    CliPutStr("\033[0m"); // 取消格式
    lastLogType = LOG_EVENT;
    WaitUsrInput();

    return 0;
}

int LogBar(const char* str, ...)
{
    char CLI_TX_BUFFER[CMD_LEN_MAX];
    va_list args;
    va_start(args, str);
    vsprintf(CLI_TX_BUFFER, str, args);
    va_end(args);
    if (lastLogType != LOG_BAR && lastLogType != LOG_NOINPUT) {
        CliPutStr("\r\n");
    }
    CliClrLine();
    CliPutStr("\033[33m"); // 设置为黄色
    CliPutStr("%s %s", CliTimeStamp(), CLI_TX_BUFFER);
    CliPutStr("\033[0m"); // 取消格式
    lastLogType = LOG_BAR;

    return 0;
}

int LOGTop(const char *str, ...)
{
    char CLI_TX_BUFFER[CMD_LEN_MAX];
    va_list args;
    va_start(args, str);
    vsprintf(CLI_TX_BUFFER, str, args);
    va_end(args);
    CliSaveCursorPos();
    CliCursorTo(0, 0);
    CliClrLine();
    CliPutStr("\033[43m"); // 设置背景为黄色
    CliPutStr("%s %s", CliTimeStamp(), CLI_TX_BUFFER);
    CliPutStr("\033[0m"); // 取消格式
    CliRestoreCursorPos();
    lastLogType = LOG_BAR;

    return 0;
}

void WaitUsrInput()
{
    if (lastLogType == LOG_NOINPUT) {
        return;
    }
    CliPutStr("\033[7m"); // 设置高亮度
    CliPutStr("\r\nCLI");
    CliPutStr("\033[0m"); // 取消格式
    CliPutStr(" > ");
    lastLogType = LOG_NOINPUT;
}

static int ExeCliCmd(const char* rxBuffer, size_t rxLen)
{
    char cmdHead[CMD_LEN_MAX] = {0};
    // 提取命令字
    sscanf(rxBuffer, "%s", cmdHead);

    for (size_t id = 0; id < BSP_CMD_LIST_LEN; id++) {
        const CmdPair* cmdSlot = BSP_CMD_LIST + id;
        if (cmdSlot == NULL) {
            continue;
        }
        if (strcmp(cmdSlot->cmd, cmdHead) == 0) {
            /* 向ctask中注册任务，ExeTask()中异步地回调 */
            size_t bodyLen = rxLen - strlen(cmdHead);
            RegTask(cmdSlot->exe, rxBuffer + strlen(cmdHead), bodyLen);
            RegTask(DefaultFlushTask, NULL, 0);
            return ERR_SUCCESS;
        }
    }

    /* 未知默认命令字处理 */
    LOG("unknown cmdHead \"%s\"\r\n\tValid Cmd List:\r\n", cmdHead);
    for (int id = 0; id < BSP_CMD_LIST_LEN; id++)
    {
        LOG(" [%u]%s\r\n", id, BSP_CMD_LIST[id].cmd);
    }
    FlushRx();
    WaitUsrInput();
    return 1;
}

void FlushRx(void)
{
    memset(CLI_RX_BUFFER, 0, CMD_LEN_MAX);
    RX_BUFF_IDX = 0;
}

void ClrVtScreen()
{
    CliPutStr("\033[1J"); // 清屏
    CliPutStr("\033[0;0H"); // 光标复位
}

int CliBackSpace(const char * str, size_t len)
{
    (void)str; (void)(len);
    if (RX_BUFF_IDX == 0) {
        return ERR_FAILURE;
    }
    CLI_RX_BUFFER[--RX_BUFF_IDX] = '\0'; // 删除BK字符本身
    if (RX_BUFF_IDX == 0) {
        return ERR_SUCCESS;
    }
    CLI_RX_BUFFER[--RX_BUFF_IDX] = '\0';
    CliPutStr("\033[1D"); // 光标左移1
    CliPutStr("\033[K"); // 删除当前光标处到行尾的内容

    return ERR_SUCCESS;
}

int CliReturn(const char * str, size_t len)
{
    CliPutStr("\r\n");
    ExeCliCmd(str, len);
    UpdateHistoryCmd();
    return ERR_SUCCESS;
}

void CliClrLine(void)
{
    CliPutStr("\033[0G"); // 光标左移到行首
    CliPutStr("\033[K"); // 删除当前光标处到行尾的内容
}

void CliCursorTo(size_t y, size_t x)
{
    CliPutStr("\033[%d;%dH", y, x);
}

void CliSaveCursorPos(void)
{
    CliPutStr("\033[s");
}

void CliRestoreCursorPos(void)
{
    CliPutStr("\033[u");
}

#define CLI_HIS_RX_LEN 3
static char CLI_HIS_RX_BUFFER[CLI_HIS_RX_LEN][CMD_LEN_MAX];
static int CLI_HIS_RX_IDX = CLI_HIS_RX_LEN - 1;
static void UpdateHistoryCmd()
{
    // 历史命令填充
    for (size_t idx = 1; idx < CLI_HIS_RX_LEN; idx++) {
        memcpy(CLI_HIS_RX_BUFFER[idx - 1], CLI_HIS_RX_BUFFER[idx], CMD_LEN_MAX);
    }
    memcpy(CLI_HIS_RX_BUFFER[CLI_HIS_RX_LEN - 1], CLI_RX_BUFFER, CMD_LEN_MAX);
}

static int CliUp(const char * str, size_t len)
{
    (void)str; (void)(len);
    if (++CLI_HIS_RX_IDX == CLI_HIS_RX_LEN) {
        CLI_HIS_RX_IDX = CLI_HIS_RX_LEN - 1;
    }
    CLI_RX_BUFFER[--RX_BUFF_IDX] = '\0'; // 删除方向键引起的0x1d
    CLI_RX_BUFFER[--RX_BUFF_IDX] = '\0'; // 删除方向键引起的'['

    CliClrLine();

    CliPutStr("\033[7m"); // 设置高亮度
    CliPutStr("CLI");
    CliPutStr("\033[0m"); // 取消格式
    const char* currHisCmd = CLI_HIS_RX_BUFFER[CLI_HIS_RX_IDX];
    memcpy(CLI_RX_BUFFER, currHisCmd, CMD_LEN_MAX);
    CliPutStr(" > %s", CLI_RX_BUFFER);
    RX_BUFF_IDX = strlen(CLI_RX_BUFFER);

    return ERR_SUCCESS;
}

static int CliDown(const char * str, size_t len)
{
    (void)str; (void)(len);
    if (--CLI_HIS_RX_IDX < 0) {
        CLI_HIS_RX_IDX = 0;
    }
    CLI_RX_BUFFER[--RX_BUFF_IDX] = '\0'; // 删除方向键引起的0x1d
    CLI_RX_BUFFER[--RX_BUFF_IDX] = '\0'; // 删除方向键引起的'['
    CliClrLine();

    CliPutStr("\033[7m"); // 设置高亮度
    CliPutStr("CLI");
    CliPutStr("\033[0m"); // 取消格式
    const char* currHisCmd = CLI_HIS_RX_BUFFER[CLI_HIS_RX_IDX];
    memcpy(CLI_RX_BUFFER, currHisCmd, CMD_LEN_MAX);
    CliPutStr(" > %s", CLI_RX_BUFFER);
    RX_BUFF_IDX = strlen(CLI_RX_BUFFER);

    return ERR_SUCCESS;
}

void PutHex(const char* str, size_t len)
{
    LOG("len = %u", len);
    size_t singleLineIdx = 32;
    for (size_t idx = 0; idx < len; idx++) {
        if (idx % singleLineIdx == 0) {
            LOG("\r\n");
        }
        LOG("%02X ", str[idx]);
    }
    LOG("\r\n");
}

void GetVerCode(char* verStr, size_t len)
{
    if (verStr == NULL || len <= 12) {
        return;
    }
    sprintf(verStr, "202305031130");
}

static int DefaultFlushTask(const char *str, size_t len)
{
    (void)str; (void)len;
    FlushRx();
    WaitUsrInput();
    return ERR_SUCCESS;
}
